package reporte;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import java.util.*;

import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;

import productosYPresentaciones.Producto;
import clientes.Cliente;
import clientes.Ticket;
import cadenaDeSucursales.CadenaSucursales;
import envios.Envio;
import exceptions.ParametrosMalColocadosException;
import sucursal.Stock;
import sucursal.Sucursal;
import ventas.Venta;
import ventas.VentaConEnvio;

/**
 * 
 * @author Octavio Nicolas Pompilio
 *
 */
public class ReporteTest {
	private Reporte reporte;
	private Sucursal sucursal;
	private Venta venta1,venta2,venta3;
	private List<Venta> lista;
	
	@Before
	public void setUp() throws Exception {
		sucursal = mock( Sucursal.class );
		
		reporte = new Reporte(sucursal);
		lista = new ArrayList<Venta>();
		
		venta1 = mock( Venta.class );
		venta2 = mock( Venta.class );
		venta3 = mock( Venta.class );
		lista.add(venta1);
		lista.add(venta2);
		lista.add(venta3);
		
		when( sucursal.getVentasRealizadas()).thenReturn( lista );
	}

	@Test
	public void testGetSucursal(){
		assertEquals( sucursal, reporte.getSucursal());
	}
	
	@Test
	public void testVentasRealizadas(){
		assertEquals( lista, reporte.ventasRealizadas());
	}
	
	@Test
	public void testReporteVentasEntreFechas() throws ParametrosMalColocadosException {
		//Como no puedo testear con mocking a las fechas, creo una
		// a conveniencia y a las demas en funcion de ella.
		DateTime fechaInicial = new DateTime(),
				fechaFinal = fechaInicial.plusDays(2);
		//El diagrama de fechas que deseo testear:
		// venta1.fecha ( venta2.fecha ) venta3.fecha
		
		when( venta1.getFechaDeCreacionDeLaVenta() ).thenReturn( fechaInicial.minusDays(1) );
		when( venta2.getFechaDeCreacionDeLaVenta() ).thenReturn( fechaInicial.plusDays(1) );
		when( venta3.getFechaDeCreacionDeLaVenta() ).thenReturn( fechaInicial.plusDays(3) );
		
		List<Venta> ventas = reporte.ventasEntreFechas(fechaInicial, fechaFinal);
		assertEquals(1,ventas.size());
		assertTrue( ventas.contains(venta2) );
	}
	
	@Test(expected = ParametrosMalColocadosException.class)
	public void testReporteVentasEntreFechasLanzaException() throws ParametrosMalColocadosException {
		DateTime fechaInicial = new DateTime(),
				fechaFinal = fechaInicial.plusDays(2);
		reporte.ventasEntreFechas(fechaFinal, fechaInicial);
	}
	
	@Test
	public void testReporteVentasEntreMontos() throws ParametrosMalColocadosException {
		float montoInicial = 10,montoFinal = 100;
		Ticket rc1 = mock(Ticket.class),
						rc2 = mock(Ticket.class),
						rc3 = mock(Ticket.class);
		
		when( venta1.getTicketDeVenta() ).thenReturn(rc1);
		when( venta2.getTicketDeVenta() ).thenReturn(rc2);
		when( venta3.getTicketDeVenta() ).thenReturn(rc3);
		when(rc1.getPrecio()).thenReturn(9f);
		when(rc2.getPrecio()).thenReturn(11f);
		when(rc3.getPrecio()).thenReturn(101f);
		
		List<Venta> ventas = reporte.ventasEntreMontos(montoInicial, montoFinal);
		assertEquals(1,ventas.size());
		assertTrue( ventas.contains(venta2) );
	}
	
	@Test(expected = ParametrosMalColocadosException.class)
	public void testReporteVentasEntreMontosLanzaException() throws ParametrosMalColocadosException {
		reporte.ventasEntreMontos(100f, 10f);
	}
	
	@Test
	public void testReporteVentasPorProducto() {
		//Se establecen los productos participantes del test
		Producto productoABuscar = mock( Producto.class ),
				productoSolitario = mock( Producto.class );
		//Se establecen la listas de productos 
		Map<Producto,Integer> listaConProductoABuscar = mock( Map.class ),
				listaConProductoSolitario = mock( Map.class );
		
		//Que productos contiene cada lista de productos
		when( listaConProductoABuscar.containsKey(productoABuscar) ).thenReturn(true);
		when( listaConProductoABuscar.containsKey(productoSolitario) ).thenReturn(false);
		when( listaConProductoSolitario.containsKey(productoABuscar) ).thenReturn(false);
		when( listaConProductoSolitario.containsKey(productoSolitario) ).thenReturn(true);
		
		
		//Se establecen las listas de compras de las ventas
		when( venta1.getProductosComprados() ).thenReturn( listaConProductoABuscar );
		when( venta2.getProductosComprados() ).thenReturn( listaConProductoABuscar );
		when( venta3.getProductosComprados() ).thenReturn( listaConProductoSolitario );
		
		List<Venta> ventas = reporte.ventasConProducto(productoABuscar);
		assertEquals( 2, ventas.size() );
		assertTrue( ventas.contains(venta1) );
		assertTrue( ventas.contains(venta2) );
	}

	@Test
	public void testReporteFiltroProductoPorDebajoDeStockMinimo() {
		Stock stock = mock( Stock.class );
		
		List<Producto> lista = new ArrayList<Producto>();
		Producto prod1 = mock( Producto.class );
		Producto prod2 = mock( Producto.class );
		Producto prod3 = mock( Producto.class );
		
		lista.add(prod1);
		lista.add(prod2);
		lista.add(prod3);
		
		when( stock.getProductosDelStock() ).thenReturn( lista );
		
		when(stock.getCantStock(prod1)).thenReturn(1);
		when(stock.getCantStock(prod2)).thenReturn(1);
		when(stock.getCantStock(prod3)).thenReturn(1);
		
		when( prod1.stockMin() ).thenReturn(1);
		when( prod2.stockMin() ).thenReturn(2);
		when( prod3.stockMin() ).thenReturn(3);
		when(sucursal.getStockProductos() ).thenReturn(stock);
		
		assertEquals(2, reporte.productosPorDebajoDeStockMin().size() );
		assertTrue(reporte.productosPorDebajoDeStockMin().contains(prod2));
		assertTrue(reporte.productosPorDebajoDeStockMin().contains(prod3));
	}
	
	@Test
	public void testReporteFiltroProductoPorDebajoDeStockCritico() {
		Stock stock = mock( Stock.class );
		
		List<Producto> lista = new ArrayList<Producto>();
		Producto prod1 = mock( Producto.class );
		Producto prod2 = mock( Producto.class );
		Producto prod3 = mock( Producto.class );
		
		lista.add(prod1);
		lista.add(prod2);
		lista.add(prod3);
		
		when( stock.getProductosDelStock() ).thenReturn( lista );
		
		when(stock.getCantStock(prod1)).thenReturn(2);
		when(stock.getCantStock(prod2)).thenReturn(2);
		when(stock.getCantStock(prod3)).thenReturn(2);
		when( prod1.stockCritico() ).thenReturn(1);
		when( prod2.stockCritico() ).thenReturn(2);
		when( prod3.stockCritico() ).thenReturn(3);

		when(sucursal.getStockProductos() ).thenReturn(stock);
		
		assertEquals(1, reporte.productosPorDebajoDeStockCritico().size() );
		assertTrue(reporte.productosPorDebajoDeStockCritico().contains(prod3));
	}
	
	@Test
	public void testgetEnvios(){
		List<Venta> listaVentasConEnvio = new ArrayList<Venta>();
		
		Venta ventasConEnvio1 = mock( VentaConEnvio.class );
		Venta ventasConEnvio2 = mock( VentaConEnvio.class );
		Venta ventasConEnvio3 = mock( VentaConEnvio.class );
		
		listaVentasConEnvio.add(ventasConEnvio1);
		listaVentasConEnvio.add(ventasConEnvio2);
		listaVentasConEnvio.add(ventasConEnvio3);
		
		List<Envio> listaEnvio = new ArrayList<Envio>();	
		Envio envio1 = mock( Envio.class ),
				envio2 =  mock( Envio.class ),
				envio3 =  mock( Envio.class );

		listaEnvio.add(envio1);
		listaEnvio.add(envio2);
		listaEnvio.add(envio3);
		
		when( ventasConEnvio1.soyUnaVentaConEnvio() ).thenReturn(true);
		when( ventasConEnvio2.soyUnaVentaConEnvio() ).thenReturn(true);
		when( ventasConEnvio3.soyUnaVentaConEnvio() ).thenReturn(false);
		
		when( ((VentaConEnvio) ventasConEnvio1).getEnvioAProcesar()).thenReturn(envio1);
		when( ((VentaConEnvio) ventasConEnvio2).getEnvioAProcesar()).thenReturn(envio2);
		when( ((VentaConEnvio) ventasConEnvio3).getEnvioAProcesar()).thenReturn(envio3);
		
		when(sucursal.getVentasRealizadas()).thenReturn(listaVentasConEnvio);
		
		List<Envio> envios = reporte.getEnvios();
		
		assertEquals( 2, envios.size() );
		assertEquals( envio1, envios.get(0) );
		assertEquals( envio2, envios.get(1) );
	}
	
	@Test
	public void testEnviosConPendientes(){
		List<Venta> listaVentasConEnvio = new ArrayList<Venta>();
		DateTime fechabase = new DateTime();
		
		Venta ventasConEnvio1 = mock( VentaConEnvio.class );
		Venta ventasConEnvio2 = mock( VentaConEnvio.class );
		Venta ventasConEnvio3 = mock( VentaConEnvio.class );
		
		listaVentasConEnvio.add(ventasConEnvio1);
		listaVentasConEnvio.add(ventasConEnvio2);
		listaVentasConEnvio.add(ventasConEnvio3);
		
		List<Envio> listaEnvio = new ArrayList<Envio>();	
		Envio envio1 = mock( Envio.class ),
				envio2 =  mock( Envio.class ),
				envio3 =  mock( Envio.class );

		when( envio1.getFecha() ).thenReturn( fechabase.plusDays(10) );
		when( envio2.getFecha() ).thenReturn( fechabase.plusDays(20) );
		when( envio3.getFecha() ).thenReturn( fechabase.plusDays(30) );
		
		//Los coloco en orden inverso para que luego lo ordene
		listaEnvio.add(envio3);
		listaEnvio.add(envio2);
		listaEnvio.add(envio1);
		
		when( ventasConEnvio1.soyUnaVentaConEnvio() ).thenReturn(true);
		when( ventasConEnvio2.soyUnaVentaConEnvio() ).thenReturn(true);
		when( ventasConEnvio3.soyUnaVentaConEnvio() ).thenReturn(true);
		
		when( ((VentaConEnvio) ventasConEnvio1).getEnvioAProcesar()).thenReturn(envio1);
		when( ((VentaConEnvio) ventasConEnvio2).getEnvioAProcesar()).thenReturn(envio2);
		when( ((VentaConEnvio) ventasConEnvio3).getEnvioAProcesar()).thenReturn(envio3);
		
		when(sucursal.getVentasRealizadas()).thenReturn(listaVentasConEnvio);
		
		when( envio1.estaPendienteDeEnvio() ).thenReturn(true);
		when( envio2.estaPendienteDeEnvio() ).thenReturn(true);
		when( envio3.estaPendienteDeEnvio() ).thenReturn(true);

		List<Envio> envios = reporte.enviospendientes();
		
		assertEquals( 3, envios.size() );
		assertEquals( envio1, envios.get(0) );
		assertEquals( envio2, envios.get(1) );
		assertEquals( envio3, envios.get(2) );
	}
	
	@Test
	public void testEnviosSinPendientes(){
		List<Venta> listaVentasConEnvio = new ArrayList<Venta>();
		DateTime fechabase = new DateTime();
		
		Venta ventasConEnvio1 = mock( VentaConEnvio.class );
		Venta ventasConEnvio2 = mock( VentaConEnvio.class );
		Venta ventasConEnvio3 = mock( VentaConEnvio.class );
		
		listaVentasConEnvio.add(ventasConEnvio1);
		listaVentasConEnvio.add(ventasConEnvio2);
		listaVentasConEnvio.add(ventasConEnvio3);
		
		List<Envio> listaEnvio = new ArrayList<Envio>();	
		Envio envio1 = mock( Envio.class ),
				envio2 =  mock( Envio.class ),
				envio3 =  mock( Envio.class );

		when( envio1.getFecha() ).thenReturn( fechabase.plusDays(10) );
		when( envio2.getFecha() ).thenReturn( fechabase.plusDays(20) );
		when( envio3.getFecha() ).thenReturn( fechabase.plusDays(30) );
		
		//Los coloco en orden inverso para que luego lo ordene
		listaEnvio.add(envio3);
		listaEnvio.add(envio2);
		listaEnvio.add(envio1);
		
		when( ventasConEnvio1.soyUnaVentaConEnvio() ).thenReturn(true);
		when( ventasConEnvio2.soyUnaVentaConEnvio() ).thenReturn(true);
		when( ventasConEnvio3.soyUnaVentaConEnvio() ).thenReturn(true);
		
		when( ((VentaConEnvio) ventasConEnvio1).getEnvioAProcesar()).thenReturn(envio1);
		when( ((VentaConEnvio) ventasConEnvio2).getEnvioAProcesar()).thenReturn(envio2);
		when( ((VentaConEnvio) ventasConEnvio3).getEnvioAProcesar()).thenReturn(envio3);
		
		when(sucursal.getVentasRealizadas()).thenReturn(listaVentasConEnvio);
		
		when( envio1.estaPendienteDeEnvio() ).thenReturn(false);
		when( envio2.estaPendienteDeEnvio() ).thenReturn(false);
		when( envio3.estaPendienteDeEnvio() ).thenReturn(false);
		
		assertTrue( reporte.enviospendientes().isEmpty() );
	}
	
	@Test
	public void testEnviosConPendientesyNoPendientes(){
		List<Venta> listaVentasConEnvio = new ArrayList<Venta>();
		DateTime fechabase = new DateTime();
		
		Venta ventasConEnvio1 = mock( VentaConEnvio.class );
		Venta ventasConEnvio2 = mock( VentaConEnvio.class );
		Venta ventasConEnvio3 = mock( VentaConEnvio.class );
		Venta ventasConEnvio4 = mock( VentaConEnvio.class );
		Venta ventasConEnvio5 = mock( VentaConEnvio.class );
		Venta ventasConEnvio6 = mock( VentaConEnvio.class );
		
		listaVentasConEnvio.add(ventasConEnvio1);
		listaVentasConEnvio.add(ventasConEnvio2);
		listaVentasConEnvio.add(ventasConEnvio3);
		listaVentasConEnvio.add(ventasConEnvio4);
		listaVentasConEnvio.add(ventasConEnvio5);
		listaVentasConEnvio.add(ventasConEnvio6);
		
		List<Envio> listaEnvio = new ArrayList<Envio>();	
		Envio envio1 = mock( Envio.class ),
				envio2 =  mock( Envio.class ),
				envio3 =  mock( Envio.class ),
				envio4 = mock( Envio.class ),
				envio5 =  mock( Envio.class ),
				envio6 =  mock( Envio.class );

		when( envio1.getFecha() ).thenReturn( fechabase.plusDays(10) );
		when( envio2.getFecha() ).thenReturn( fechabase.plusDays(20) );
		when( envio3.getFecha() ).thenReturn( fechabase.plusDays(30) );
		when( envio4.getFecha() ).thenReturn( fechabase.plusDays(1) );
		when( envio5.getFecha() ).thenReturn( fechabase.plusDays(2) );
		when( envio6.getFecha() ).thenReturn( fechabase.plusDays(60) );
		
		//Los coloco al azar
		listaEnvio.add(envio1);
		listaEnvio.add(envio6);
		listaEnvio.add(envio4);
		listaEnvio.add(envio5);
		listaEnvio.add(envio3);
		listaEnvio.add(envio2);
		
		//Declaro que todas son ventas con envios
		when( ventasConEnvio1.soyUnaVentaConEnvio() ).thenReturn(true);
		when( ventasConEnvio2.soyUnaVentaConEnvio() ).thenReturn(true);
		when( ventasConEnvio3.soyUnaVentaConEnvio() ).thenReturn(true);
		when( ventasConEnvio4.soyUnaVentaConEnvio() ).thenReturn(true);
		when( ventasConEnvio5.soyUnaVentaConEnvio() ).thenReturn(true);
		when( ventasConEnvio6.soyUnaVentaConEnvio() ).thenReturn(true);
		
		when( ((VentaConEnvio) ventasConEnvio1).getEnvioAProcesar()).thenReturn(envio1);
		when( ((VentaConEnvio) ventasConEnvio2).getEnvioAProcesar()).thenReturn(envio2);
		when( ((VentaConEnvio) ventasConEnvio3).getEnvioAProcesar()).thenReturn(envio3);
		when( ((VentaConEnvio) ventasConEnvio4).getEnvioAProcesar()).thenReturn(envio4);
		when( ((VentaConEnvio) ventasConEnvio5).getEnvioAProcesar()).thenReturn(envio5);
		when( ((VentaConEnvio) ventasConEnvio6).getEnvioAProcesar()).thenReturn(envio6);
		
		when(sucursal.getVentasRealizadas()).thenReturn(listaVentasConEnvio);
		
		when( envio1.estaPendienteDeEnvio() ).thenReturn(true);
		when( envio2.estaPendienteDeEnvio() ).thenReturn(true);
		when( envio3.estaPendienteDeEnvio() ).thenReturn(false);
		when( envio4.estaPendienteDeEnvio() ).thenReturn(false);
		when( envio5.estaPendienteDeEnvio() ).thenReturn(true);
		when( envio6.estaPendienteDeEnvio() ).thenReturn(true);
		
		List<Envio> envios = reporte.enviospendientes();
		
		assertEquals( 4, envios.size() );
		assertEquals( envio5, envios.get(0) );
		assertEquals( envio1, envios.get(1) );
		assertEquals( envio2, envios.get(2) );
		assertEquals( envio6, envios.get(3) );
	}
	
	@Test
	public void testCuentaCcPesos(){
		Ticket ticketc11 = mock( Ticket.class ),
				ticketc12 = mock( Ticket.class ),
				ticketc21 = mock( Ticket.class ),
				ticketc22 = mock( Ticket.class ),
				ticketc23 = mock( Ticket.class ),
				ticketc24 = mock( Ticket.class );
		
		List<Cliente> clientes = new ArrayList<Cliente>();
		Cliente c1 = mock( Cliente.class ),
				c2 = mock( Cliente.class );
		clientes.add(c1);
		clientes.add(c2);
		
		List<Ticket> listaCliente1 = new ArrayList<Ticket>(),
				listaCliente2 = new ArrayList<Ticket>();
		
		listaCliente1.add(ticketc11);
		listaCliente1.add(ticketc12);
		
		listaCliente2.add(ticketc21);
		listaCliente2.add(ticketc22);
		listaCliente2.add(ticketc23);
		listaCliente2.add(ticketc24);
	
		when( c1.getTickets() ).thenReturn(listaCliente1);
		when( c2.getTickets() ).thenReturn(listaCliente2);
		
		CadenaSucursales cadena = mock( CadenaSucursales.class );
		when( cadena.getClientes()).thenReturn(clientes);
		when( sucursal.getCadena()).thenReturn(cadena);
		
		Map<Cliente,List<Ticket> > cuentaCcPesos = reporte.cuentaCcPesos();
		assertEquals(2, cuentaCcPesos.size() );
		assertTrue(cuentaCcPesos.containsKey(c1));
		assertTrue(cuentaCcPesos.containsKey(c2));
		
		assertEquals(listaCliente1, cuentaCcPesos.get(c1));
		assertEquals(listaCliente2, cuentaCcPesos.get(c2));
	}
	
	@Test
	public void testCuentaCcProductos(){
		Ticket ticketc11 = mock( Ticket.class ),
				ticketc12 = mock( Ticket.class ),
				ticketc21 = mock( Ticket.class ),
				ticketc22 = mock( Ticket.class ),
				ticketc23 = mock( Ticket.class ),
				ticketc24 = mock( Ticket.class );
		
		List<Cliente> clientes = new ArrayList<Cliente>();
		Cliente c1 = mock( Cliente.class ),
				c2 = mock( Cliente.class );
		clientes.add(c1);
		clientes.add(c2);
		
		List<Ticket> listaCliente1 = new ArrayList<Ticket>(),
				listaCliente2 = new ArrayList<Ticket>();
		
		listaCliente1.add(ticketc11);
		listaCliente1.add(ticketc12);
		
		listaCliente2.add(ticketc21);
		listaCliente2.add(ticketc22);
		listaCliente2.add(ticketc23);
		listaCliente2.add(ticketc24);
	
		when( c1.getTickets() ).thenReturn(listaCliente1);
		when( c2.getTickets() ).thenReturn(listaCliente2);
		
		CadenaSucursales cadena = mock( CadenaSucursales.class );
		when( cadena.getClientes()).thenReturn(clientes);
		when( sucursal.getCadena()).thenReturn(cadena);
		
		Map<Cliente,List<Ticket> > cuentaCcPesos = reporte.cuentaCcProductos();
		assertEquals(2, cuentaCcPesos.size() );
		assertTrue(cuentaCcPesos.containsKey(c1));
		assertTrue(cuentaCcPesos.containsKey(c2));
		
		assertEquals(listaCliente1, cuentaCcPesos.get(c1));
		assertEquals(listaCliente2, cuentaCcPesos.get(c2));
	}
	
}
